
`include "VX_define.v"

module VX_decode(
	// Fetch Inputs
	VX_inst_meta_inter     fd_inst_meta_de,

	// Outputs
	VX_frE_to_bckE_req_inter VX_frE_to_bckE_req,
	VX_wstall_inter          VX_wstall,
	VX_join_inter            VX_join,

	output wire              terminate_sim

);

		wire[31:0]      in_instruction     = fd_inst_meta_de.instruction;
		wire[31:0]      in_curr_PC         = fd_inst_meta_de.inst_pc;
		wire[`NW_M1:0]  in_warp_num        = fd_inst_meta_de.warp_num;

		assign VX_frE_to_bckE_req.curr_PC  = in_curr_PC;

		wire[`NT_M1:0]  in_valid = fd_inst_meta_de.valid;

		wire[6:0]  curr_opcode;

		wire       is_itype;
		wire       is_rtype;
		wire       is_stype;
		wire       is_btype;
		wire       is_linst;
		wire       is_jal;
		wire       is_jalr;
		wire       is_lui;
		wire       is_auipc;
		wire       is_csr;
		wire       is_csr_immed;
		wire       is_e_inst;

		wire       is_gpgpu;
		wire       is_wspawn;
		wire       is_tmc;
		wire       is_split;
		wire       is_join;
		wire       is_barrier;

		wire[2:0]  func3;
		wire[6:0]  func7;
		wire[11:0] u_12;


		wire[7:0]  jal_b_19_to_12;
		wire       jal_b_11;
		wire[9:0]  jal_b_10_to_1;
		wire       jal_b_20;
		wire       jal_b_0;
		wire[20:0] jal_unsigned_offset;
		wire[31:0] jal_1_offset;

		wire[11:0] jalr_immed;
		wire[31:0] jal_2_offset;

		wire       jal_sys_cond1;
		wire       jal_sys_cond2;
		wire       jal_sys_jal;
		wire[31:0] jal_sys_off;

		wire       csr_cond1;
		wire       csr_cond2;

		wire[11:0] alu_tempp;
		wire       alu_shift_i;
		wire[11:0] alu_shift_i_immed;

		wire[1:0]  csr_type;

		reg[4:0]   csr_alu;
		reg[4:0]   alu_op;
		reg[4:0]   mul_alu;
		reg[19:0]  temp_upper_immed;
		reg       temp_jal;
		reg[31:0] temp_jal_offset;
		reg[31:0] temp_itype_immed;
		reg[2:0] temp_branch_type;
		reg      temp_branch_stall;

		// always @(posedge reset) begin
		
		// end

		assign VX_frE_to_bckE_req.valid    = fd_inst_meta_de.valid;

		assign VX_frE_to_bckE_req.warp_num = in_warp_num;


		assign curr_opcode    = in_instruction[6:0];


		assign VX_frE_to_bckE_req.rd   = in_instruction[11:7];
		assign VX_frE_to_bckE_req.rs1  = in_instruction[19:15];
		assign VX_frE_to_bckE_req.rs2  = in_instruction[24:20];
		assign func3    = in_instruction[14:12];
		assign func7    = in_instruction[31:25];
		assign u_12     = in_instruction[31:20];


		assign VX_frE_to_bckE_req.PC_next = in_curr_PC + 32'h4;


		// Write Back sigal
		assign is_rtype     = (curr_opcode == `R_INST);
		assign is_linst     = (curr_opcode == `L_INST);
		assign is_itype     = (curr_opcode == `ALU_INST) || is_linst; 
		assign is_stype     = (curr_opcode == `S_INST);
		assign is_btype     = (curr_opcode == `B_INST);
		assign is_jal       = (curr_opcode == `JAL_INST);
		assign is_jalr      = (curr_opcode == `JALR_INST);
		assign is_lui       = (curr_opcode == `LUI_INST);
		assign is_auipc     = (curr_opcode == `AUIPC_INST);
		assign is_csr       = (curr_opcode == `SYS_INST) && (func3 != 0);
		assign is_csr_immed = (is_csr) && (func3[2] == 1);
		// assign is_e_inst    = (curr_opcode == `SYS_INST) && (func3 == 0);
		assign is_e_inst    = in_instruction == 32'h00000073;

		assign is_gpgpu     = (curr_opcode == `GPGPU_INST);

		assign is_tmc       = is_gpgpu && (func3 == 0); // Goes to BE
		assign is_wspawn    = is_gpgpu && (func3 == 1); // Goes to BE
		assign is_barrier   = is_gpgpu && (func3 == 4); // Goes to BE
		assign is_split     = is_gpgpu && (func3 == 2); // Goes to BE
		assign is_join      = is_gpgpu && (func3 == 3); // Doesn't go to BE


		assign VX_join.is_join       = is_join;
		assign VX_join.join_warp_num = in_warp_num;


		assign VX_frE_to_bckE_req.is_wspawn  = is_wspawn;
		assign VX_frE_to_bckE_req.is_tmc     = is_tmc;
		assign VX_frE_to_bckE_req.is_split   = is_split;
		assign VX_frE_to_bckE_req.is_barrier = is_barrier;



		assign VX_frE_to_bckE_req.csr_immed = is_csr_immed;
		assign VX_frE_to_bckE_req.is_csr    = is_csr;


		assign VX_frE_to_bckE_req.wb       = (is_jal || is_jalr || is_e_inst) ? `WB_JAL :
			                   					is_linst ? `WB_MEM :
			                   	     				(is_itype || is_rtype || is_lui || is_auipc || is_csr) ?  `WB_ALU :
			                   	     	    			`NO_WB;


		assign VX_frE_to_bckE_req.rs2_src   = (is_itype || is_stype) ? `RS2_IMMED : `RS2_REG;

		// MEM signals 
		assign VX_frE_to_bckE_req.mem_read  = (is_linst) ? func3 : `NO_MEM_READ;
		assign VX_frE_to_bckE_req.mem_write = (is_stype) ? func3 : `NO_MEM_WRITE;

		// UPPER IMMEDIATE
		always @(*) begin
			case(curr_opcode)
				`LUI_INST:   temp_upper_immed  = {func7, VX_frE_to_bckE_req.rs2, VX_frE_to_bckE_req.rs1, func3};
				`AUIPC_INST: temp_upper_immed  = {func7, VX_frE_to_bckE_req.rs2, VX_frE_to_bckE_req.rs1, func3};
				default:     temp_upper_immed  = 20'h0;
			endcase // curr_opcode
		end

		assign VX_frE_to_bckE_req.upper_immed = temp_upper_immed;


		assign jal_b_19_to_12      = in_instruction[19:12];
		assign jal_b_11            = in_instruction[20];
		assign jal_b_10_to_1       = in_instruction[30:21];
		assign jal_b_20            = in_instruction[31];
		assign jal_b_0             = 1'b0;
		assign jal_unsigned_offset = {jal_b_20, jal_b_19_to_12, jal_b_11, jal_b_10_to_1, jal_b_0};
		assign jal_1_offset         = {{11{jal_b_20}}, jal_unsigned_offset};


		assign jalr_immed   = {func7, VX_frE_to_bckE_req.rs2};
		assign jal_2_offset = {{20{jalr_immed[11]}}, jalr_immed};


		assign jal_sys_cond1 = func3 == 3'h0;
		assign jal_sys_cond2 = u_12  < 12'h2;

		assign jal_sys_jal = (jal_sys_cond1 && jal_sys_cond2) ? 1'b1 : 1'b0;
		assign jal_sys_off = (jal_sys_cond1 && jal_sys_cond2) ? 32'hb0000000 : 32'hdeadbeef;

		// JAL 
		always @(*) begin
			case(curr_opcode)
				`JAL_INST:
					begin
		       		 	temp_jal        = 1'b1 && (|in_valid);
						temp_jal_offset = jal_1_offset;
					end
				`JALR_INST:
					begin
		        		temp_jal        = 1'b1 && (|in_valid);
						temp_jal_offset = jal_2_offset;
					end
				`SYS_INST:
					begin
						// $display("SYS EBREAK %h", (jal_sys_jal && (|in_valid)) );
						temp_jal        = jal_sys_jal && (|in_valid);
						temp_jal_offset = jal_sys_off;
					end
				default:
					begin
						temp_jal          = 1'b0 && (|in_valid);
						temp_jal_offset   = 32'hdeadbeef;
					end
			endcase
		end

		assign VX_frE_to_bckE_req.jalQual    = is_jal;
		assign VX_frE_to_bckE_req.jal        = temp_jal;
		assign VX_frE_to_bckE_req.jal_offset = temp_jal_offset;

		// wire is_ebreak;


		// assign is_ebreak = is_e_inst;
		wire ebreak = (curr_opcode == `SYS_INST) && (jal_sys_jal && (|in_valid));
		assign VX_frE_to_bckE_req.ebreak = ebreak;
		wire out_ebreak = ebreak;
		assign terminate_sim = is_e_inst;


		// CSR

		assign csr_cond1  = func3 != 3'h0;
		assign csr_cond2  = u_12  >= 12'h2;

		assign VX_frE_to_bckE_req.csr_address = (csr_cond1 && csr_cond2) ? u_12 : 12'h55;


		// ITYPE IMEED
		assign alu_shift_i       = (func3 == 3'h1) || (func3 == 3'h5);
		assign alu_shift_i_immed = {{7{1'b0}}, VX_frE_to_bckE_req.rs2};
		assign alu_tempp = alu_shift_i ? alu_shift_i_immed : u_12;


		always @(*) begin
			case(curr_opcode)
					`ALU_INST: temp_itype_immed = {{20{alu_tempp[11]}}, alu_tempp};
					`S_INST:   temp_itype_immed = {{20{func7[6]}}, func7, VX_frE_to_bckE_req.rd};
					`L_INST:   temp_itype_immed = {{20{u_12[11]}}, u_12};
					`B_INST:   temp_itype_immed = {{20{in_instruction[31]}}, in_instruction[31], in_instruction[7], in_instruction[30:25], in_instruction[11:8]};
					default:   temp_itype_immed = 32'hdeadbeef;
				endcase
		end

		assign VX_frE_to_bckE_req.itype_immed = temp_itype_immed;
	


		always @(*) begin
			case(curr_opcode)
				`B_INST:
					begin
						// $display("BRANCH IN DECODE");
						temp_branch_stall = 1'b1 && (|in_valid);
						case(func3)
							3'h0: temp_branch_type = `BEQ;
							3'h1: temp_branch_type = `BNE;
							3'h4: temp_branch_type = `BLT;
							3'h5: temp_branch_type = `BGT;
							3'h6: temp_branch_type = `BLTU;
							3'h7: temp_branch_type = `BGTU;
							default: temp_branch_type = `NO_BRANCH; 
						endcase
					end

				`JAL_INST:
					begin
						temp_branch_type  = `NO_BRANCH;
						temp_branch_stall = 1'b1 && (|in_valid);
					end
				`JALR_INST:
					begin
						temp_branch_type  = `NO_BRANCH;
						temp_branch_stall = 1'b1 && (|in_valid);
					end
				default:
					begin
						temp_branch_type  = `NO_BRANCH;
						temp_branch_stall = 1'b0 && (|in_valid);
					end
			endcase
		end

		assign VX_frE_to_bckE_req.branch_type = temp_branch_type;

		assign VX_wstall.wstall               = (temp_branch_stall || is_tmc || is_split || is_barrier) && (|in_valid);
		assign VX_wstall.warp_num             = in_warp_num;

		always @(*) begin
			// ALU OP
			case(func3)
				3'h0:    alu_op = (curr_opcode == `ALU_INST) ? `ADD : (func7 == 7'h0 ? `ADD : `SUB);
				3'h1:    alu_op = `SLLA;
				3'h2:    alu_op = `SLT;
				3'h3:    alu_op = `SLTU;
				3'h4:    alu_op = `XOR;
				3'h5:    alu_op = (func7 == 7'h0) ? `SRL : `SRA;
				3'h6:    alu_op = `OR;
				3'h7:    alu_op = `AND;
				default: alu_op = `NO_ALU; 
			endcase
		end

		always @(*) begin
			// ALU OP
			case(func3)
				3'h0:    mul_alu = `MUL;
				3'h1:    mul_alu = `MULH;
				3'h2:    mul_alu = `MULHSU;
				3'h3:    mul_alu = `MULHU;
				3'h4:    mul_alu = `DIV;
				3'h5:    mul_alu = `DIVU;
				3'h6:    mul_alu = `REM;
				3'h7:    mul_alu = `REMU;
				default: mul_alu = `NO_ALU; 
			endcase
		end

		assign csr_type = func3[1:0];

		always @(*) begin
			case(csr_type)
				2'h1:     csr_alu = `CSR_ALU_RW;
				2'h2:     csr_alu = `CSR_ALU_RS;
				2'h3:     csr_alu = `CSR_ALU_RC;
				default:  csr_alu = `NO_ALU;
			endcase
		end

		wire[4:0] temp_final_alu;

		assign temp_final_alu = is_btype ? ((VX_frE_to_bckE_req.branch_type < `BLTU) ? `SUB : `SUBU) :
										is_lui ? `LUI_ALU :
											is_auipc ? `AUIPC_ALU :
												is_csr ? csr_alu :
													(is_stype || is_linst) ? `ADD :
														alu_op;

		assign VX_frE_to_bckE_req.alu_op = ((func7[0] == 1'b1) && is_rtype) ? mul_alu : temp_final_alu;

endmodule








